/**
 * 渲染函数、diff算法文件
 */
// 简单渲染函数, 生成虚拟DOM
const h = (tag, props, children) => {
    return {
        tag,
        props,
        children
    }
}

// 简单挂载函数， 将虚拟DOM转为真实DOM
const mount = (vnode, container) => {
    // 1.创建出真实DOM元素，并在虚拟DOM（vnode）中保留el
    const el = vnode.el = document.createElement(vnode.tag)
    // 2. 处理props
    if (vnode.props) {
        for (const key in vnode.props) {
            const value = vnode.props[key]
            if (key.startsWith('on')) { // 属性以'on'开头代表是需要事件监听的
                el.addEventListener(key.slice('2').toLocaleLowerCase(), value) // 转为小写事件监听
            } else { // 普通属性
                el.setAttribute(key, value)
            }
        }
    }
    // 3. 处理children
    if (vnode.children) {
        if (typeof vnode.children === 'string') { // 如果children 是字符串，直接赋值给el
            el.textContent = vnode.children
        } else if (Array.isArray(vnode.children)) { // 如果是数组，代表是子vnode
            vnode.children.forEach(item => {
                mount(item, el) // 递归处理
            })
        } else { } // 其他情况如插槽等等，这里暂不考虑

        
    }

    // 4. 将el挂载到container上
    container.appendChild(el)
}

// n1: 旧vnode, n2: 新vnode
const patch = (n1, n2) => {
    if (n1.tag !== n2.tag) { // 两个vnode不一样，移除n1
        const n1ElParent = n1.el.parentElement // 拿到n1的父DOM元素
        n1ElParent.removeChild(n1.el) // 移除n1
        mount(n2, n1ElParent) // 将n2挂载回去
    } else {
        // 1. 取出element对象， 并在n2中保留
        const el = n2.el = n1.el

        // 2.处理props
        const oldProps = n1.props || {}
        const newProps = n2.props || {}
        // 2.1 获取所有的newProps添加到el
        for (const key in newProps) {
            const oldValue = oldProps[key]
            const newValue = newProps[key]
            if (newValue !== oldValue) { // 如果 新属性值 跟 旧属性值 不一样的话，将新的全部增加进去
                if (key.startsWith('on')) { // 属性以'on'开头代表是需要事件监听的
                    if (oldValue) { // 先把旧的事件解绑
                        el.removeEventListener(key.slice('2').toLocaleLowerCase(), oldValue) // 转为小写事件监听
                    }
                    el.addEventListener(key.slice('2').toLocaleLowerCase(), newValue) // 转为小写事件监听
                } else { // 普通属性
                    el.setAttribute(key, newValue)
                }
            }
        }
        // 2.2 删除旧的props
        for (const key in oldProps) {
            if (!(key in newProps)) { // 如果旧的属性 不存在于 新的属性中，进行移除
                const oldValue = oldProps[key]
                if (key.startsWith('on')) { // 属性以'on'开头代表是需要事件监听的
                    el.removeEventListener(key.slice('2').toLocaleLowerCase(), oldValue) // 转为小写事件监听
                } else { // 普通属性
                    el.removeAttribute(key)
                }
            }
        }

        // 3.处理children
        const oldChildren = n1.children || []
        const newChildren = n2.children || []

        if (typeof newChildren === 'string') { // 内容如果为字符串，直接替换
            if (typeof oldChildren === 'string') {
                if (newChildren !== oldChildren) {
                    el.textContent = newChildren
                }
            } else {
                el.innerHTML = newChildren
            }
        } else if (Array.isArray(newChildren)) { // 子内容为数组时
            if (typeof oldChildren === 'string') { // 如果旧的vnode的children是字符串时需要先情况内容，再把新的children重新挂载到el中
                el.innerHTML = ''
                newChildren.forEach(item => {
                    mount(item, el)
                })
            } else if (Array.isArray(oldChildren)) { // 如果旧的vnode的children是数组时
                // a.这里假设不考虑key，模拟源码中 patchUnkeyedChildren()方法， 取新、旧两个短的长度进行patch（）判断
                const commonLength = Math.min(oldChildren.length, newChildren.length)
                for (let i = 0; i < commonLength; i++ ) {
                    patch(oldChildren[i], newChildren[i]) // 递归处理 数组中同个位置
                }
                // b. 新的children长度 大于 旧的children长度，则新增挂载进去
                if (newChildren.length > oldChildren.length) {
                    newChildren.slice(oldChildren.length).forEach(item => {
                        mount(item, el)
                    })
                }
                // c. 新的children长度 小于 旧的children长度，则移除旧的children中多余出来的
                if (newChildren.length < oldChildren.length) {
                    oldChildren.slice(newChildren.length).forEach(item => {
                        el.removeChild(item.el)
                    })
                }


            } else { } // 其他情况如插槽等等，这里暂不考虑 
        } else { } // 其他情况如插槽等等，这里暂不考虑 
    }
}